//
// Created by mio on 25-5-19.
//

#include "AVDEMuxer.h"

#include "AACDecoder.h"
#include "H264Decoder.h"

AVDEMuxer::AVDEMuxer(AVContext *ac) : avContext_(ac), avDict_(nullptr), readthread_(nullptr)
{
    aacDecoder_.reset(new AACDecoder(avContext_));
    h264Decoder_.reset(new H264Decoder(avContext_));

    av_dict_set(&avDict_, "stimeout", "1000000", 0);        // Socket 级别的读写超时时间（microseconds）
    av_dict_set(&avDict_, "analyzeduration", "0", 0);       // Demuxer（解复用器）最大分析输入流格式的时长上限 (microseconds)
                                                                            // 在打开输入时，FFmpeg 会读取一段数据来自动探测（probe）流的格式、码率等信息。
                                                                            // 该参数限制了最多读取多少微秒的数据用于分析，超过就停止探测，开始解码。
    av_dict_set(&avDict_, "max_analyze_duration", "10", 0); // 作用与 analyzeduration 一致，用于兼容老版本代码
    av_dict_set(&avDict_, "fflags", "nobuffer", 0);         // 禁用输入流的内部特征缓存（buffering），直接从网络或设备拉取数据，减少延迟
    av_dict_set(&avDict_, "flags", "low_delay", 0);         // 设置 AVFormatContext 或 AVCodecContext 的通用 “低延迟” 标志
                                                                            // 在解码时尽量减少延迟，比如关闭B帧重排序等，适合对实时性要求高的场景
    av_dict_set(&avDict_, "tune", "zerolatency", 0);        // 这是给编码器（如 libx264）传递的调优选项
                                                                            // 针对零延迟场景优化编码参数，例如关闭帧间缓冲、减少码率波动等，使编码更快速、延迟更低。
    av_dict_set(&avDict_, "reconnect", "0", 0);             // 控制 FFmpeg 在输入网络断开后的自动重连行为
                                                                            // 0 表示不重连；若设为 1 则遇到网络中断时会尝试重新连接
    av_dict_set(&avDict_, "reconnect_streamed", "0", 0);    // 仅对分段或非实时流有效，控制断线后是否重新连接流式输入
                                                                            // 0 关闭；1 开启自动重连
    av_dict_set(&avDict_, "reconnect_delay_max", "5", 0);   // 如果启用了重连，上次断开到下一次重连之间的最大等待时间（seconds）
                                                                            // 在多次重连尝试时，每次重连的时间间隔会逐渐增加，但不会超过此参数指定的秒数

    pFormateCtx_ = avformat_alloc_context();
    pFormateCtx_->max_ts_probe = 50;
    pFormateCtx_->probesize = 500000;
    pFormateCtx_->interrupt_callback.callback = InterruptFouction;
    pFormateCtx_->interrupt_callback.opaque = this;
    pFormateCtx_->flags |= AVFMT_FLAG_DISCARD_CORRUPT;
}

AVDEMuxer::~AVDEMuxer()
{
    Close();
}

bool AVDEMuxer::Open(const std::string &path)
{
    readthread_.reset(new std::thread([this, path]() {
        this->FetchStream(path);
    }));
    return true;
}

void AVDEMuxer::Close()
{
    quit_ = true;
    if (avDict_) {
        av_dict_free(&avDict_);
    }

    if (readthread_) {
        if (readthread_->joinable()) {
            readthread_->join();
            readthread_.reset();
            readthread_ = nullptr;
        }
    }

    if (pFormateCtx_) {
        avformat_close_input(&pFormateCtx_);
        pFormateCtx_ = nullptr;
    }
}

void AVDEMuxer::FetchStream(const std::string &path)
{
    bool ret = true;
    if (streamCb_)
    {
        ret = FetchStreamInfo(path);
        streamCb_(ret);
    }
    if (!ret) return;

    AVPacketPtr pkt = nullptr;
    while (!quit_ && pFormateCtx_) {
        pkt = AVPacketPtr(av_packet_alloc(), [](AVPacket* p) { av_packet_free(&p); });
        // 开始读取
        int r = av_read_frame(pFormateCtx_, pkt.get());
        if (r == 0) // 读取成功
        {
            // 将数据传到音视频解码队列中
            if (pkt->stream_index == videoIndex) { h264Decoder_->put_packet(pkt); }
            else if (pkt->stream_index == audioIndex) { aacDecoder_->put_packet(pkt); }
            else { av_packet_unref(pkt.get()); }
        }
        else
        {
            av_packet_unref(pkt.get());
            break;
        }
    }
}

bool AVDEMuxer::FetchStreamInfo(const std::string &path)
{
    if (avformat_open_input(&pFormateCtx_, path.c_str(), nullptr, &avDict_) != 0) { return false; }

    if (avformat_find_stream_info(pFormateCtx_, &avDict_) < 0) { return false; }

    for (int i = 0; i < pFormateCtx_->nb_streams; i++) {
        if (pFormateCtx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoIndex = i;
            avContext_->videoDuration = pFormateCtx_->streams[i]->duration * av_q2d(pFormateCtx_->streams[i]->time_base);
        }
        else if (pFormateCtx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            audioIndex = i;
            avContext_->audioDuration = pFormateCtx_->streams[i]->duration * av_q2d(pFormateCtx_->streams[i]->time_base);
        }
    }

    if (videoIndex != -1)
    {
        // 初始化视频解码器
        if (h264Decoder_->Open(pFormateCtx_->streams[videoIndex]->codecpar) != 0)
        {
            h264Decoder_.release();
            h264Decoder_ = nullptr;
            return false;
        }
    }
    if (audioIndex != -1)
    {
        // 初始化音频解码器
        if (aacDecoder_->Open(pFormateCtx_->streams[audioIndex]->codecpar) != 0)
        {
            aacDecoder_.release();
            aacDecoder_ = nullptr;
            return false;
        }
    }
    return true;
}

double AVDEMuxer::audioDuration()
{
    // 音频时长
    return avContext_->audioDuration;
}

double AVDEMuxer::videoDuration()
{
    return avContext_->videoDuration;
}

int AVDEMuxer::InterruptFouction(void *arg)
{
    AVDEMuxer* thiz = (AVDEMuxer*)arg;
    return thiz->quit_;
}
